package com.whmall.service.crm.module.supplierreview.impl;

import com.whmall.component.entity.dto.OperatorReqDTO;
import com.whmall.component.util.FlatListUtils;
import com.whmall.service.crm.entity.dto.supplierreview.CreateSupplierReviewReqDTO;
import com.whmall.service.crm.entity.dto.supplierreview.UpdateSupplierReviewReqDTO;
import com.whmall.service.crm.module.supplierreview.SupplierReviewBusinessTypeEnum;
import com.whmall.service.crm.module.supplierreview.SupplierReviewPageSourceEnum;
import com.whmall.service.crm.module.supplierreview.SupplierReviewQueryCriteria;
import com.whmall.service.crm.module.supplierreview.SupplierReviewService;
import com.whmall.service.crm.module.supplierreview.entity.SupplierReviewAttachmentDO;
import com.whmall.service.crm.module.supplierreview.entity.SupplierReviewDO;
import com.whmall.service.crm.module.supplierreview.entity.SupplierReviewInformationDO;
import com.whmall.service.crm.module.supplierreview.impl.dao.SupplierReviewAttachmentMapper;
import com.whmall.service.crm.module.supplierreview.impl.dao.SupplierReviewInformationMapper;
import com.whmall.service.crm.module.supplierreview.impl.dao.SupplierReviewMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * SupplierReviewServiceImpl
 *
 * @author alex
 * @since 2021/1/18
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SupplierReviewServiceImpl implements SupplierReviewService {
    private final SupplierReviewMapper supplierReviewMapper;
    private final SupplierReviewInformationMapper supplierReviewInformationMapper;
    private final SupplierReviewAttachmentMapper supplierReviewAttachmentMapper;
    private final SupplierReviewTagStatisticsSynchronizer statisticsSynchronizer;

    @Transactional
    @Override
    public Long create(OperatorReqDTO requester, CreateSupplierReviewReqDTO request) {
        validateRequester(requester);
        validateCreateRequest(request);

        //
        // 评论表
        SupplierReviewDO review = new SupplierReviewDO();
        review.setCompanyId(request.getCompanyId());
        review.setPageSource(request.getPageSource().getValue());
        review.setBusinessType(request.getBusinessType().getValue());
        review.setBusinessId(request.getBusinessId());
        review.setBusinessNo(request.getBusinessNo());
        review.setTagList(FlatListUtils.toFlatString(request.getTags().stream()
                .distinct()
                .collect(Collectors.toList())));
        review.setCreateType(requester.getOperatorType().getValue());
        review.setCreateId(requester.getOperatorId());
        review.setCreateTime(new Date());
        supplierReviewMapper.insertSelective(review);

        //
        // 评论内容表
        SupplierReviewInformationDO reviewInformation = new SupplierReviewInformationDO();
        reviewInformation.setId(review.getId());
        reviewInformation.setContent(request.getContent());
        supplierReviewInformationMapper.insertSelective(reviewInformation);

        //
        // 评论附件表
        insertAttachments(review.getId(), request.getAttachmentUrls());

        return review.getId();
    }

    @Transactional
    @Override
    public void update(OperatorReqDTO requester, Long reviewId, UpdateSupplierReviewReqDTO request) {
        Assert.notNull(reviewId, "评论ID不能为空");
        validateRequester(requester);
        validateUpdateRequest(request);

        SupplierReviewDO reviewEntity = getSupplierReview(reviewId);
        revalidateTagStats(request, reviewEntity);

        //
        // 评论表
        SupplierReviewDO updateReview = new SupplierReviewDO();
        updateReview.setId(reviewId);
        updateReview.setTagList(FlatListUtils.toFlatString(request.getTags().stream()
                .distinct()
                .collect(Collectors.toList())));
        updateReview.setUpdateType(requester.getOperatorType().getValue());
        updateReview.setUpdateId(requester.getOperatorId());
        updateReview.setUpdateTime(new Date());
        updateReview.setBeforeUpdateTimeAsVersion(reviewEntity.getUpdateTime());
        int affectRows = supplierReviewMapper.optimisticUpdateByIdSelective(updateReview);
        if (affectRows != 1) {
            log.info("更新评论 {} 失败，可能的原因是该评论正在被统计，多个事务尝试对该评论进行修改导致的并发冲突", reviewId);
            throw new IllegalArgumentException("更新失败，请稍后再试！");
        }

        //
        // 评论内容表
        SupplierReviewInformationDO updateReviewInformation = new SupplierReviewInformationDO();
        updateReviewInformation.setId(reviewId);
        updateReviewInformation.setContent(request.getContent());
        supplierReviewInformationMapper.updateById(updateReviewInformation);

        //
        // 评论附件
        revalidateAttachmentUrls(reviewId, request.getAttachmentUrls());
    }

    @Transactional
    @Override
    public void remove(OperatorReqDTO requester, Long reviewId) {
        Assert.notNull(requester, "请求者不能为空");
        Assert.notNull(reviewId, "评论ID不能为空");

        SupplierReviewDO reviewEntity = getSupplierReview(reviewId);
        SupplierReviewDO update = new SupplierReviewDO();
        update.setId(reviewId);
        update.setIsDeleted(true);
        update.setUpdateType(requester.getOperatorType().getValue());
        update.setUpdateId(requester.getOperatorId());
        update.setUpdateTime(new Date());
        update.setBeforeUpdateTimeAsVersion(reviewEntity.getUpdateTime());

        int affectRows = supplierReviewMapper.optimisticUpdateByIdSelective(update);
        if (affectRows != 1) {
            log.info("软删除评论 {} 失败，可能的原因是该评论正在被统计或修改，多个事务尝试对该评论进行修改导致的并发冲突", reviewId);
            throw new IllegalArgumentException("删除失败，请稍后再试");
        }
    }

    @Override
    public void syncTagStatistics() {
        statisticsSynchronizer.sync();
    }

    @Override
    public Map<Long, Long> countByBusinessId(List<Long> businessIds, List<Integer> businessTypes, List<Integer> pageSources) {
        return supplierReviewMapper.findByQuertCriteria(SupplierReviewQueryCriteria.builder()
                .businessIdIn(businessIds)
                .businessTypeIn(businessTypes)
                .pageSourceIn(pageSources)
                .build()).stream().collect(Collectors.groupingBy(SupplierReviewDO::getBusinessId, Collectors.counting()));
    }

    /**
     * 重新设置统计标签
     *
     * @param request      更新请求
     * @param reviewEntity 评论实体
     */
    private void revalidateTagStats(UpdateSupplierReviewReqDTO request, SupplierReviewDO reviewEntity) {
        if (reviewEntity.getIsCounted()) {
            //
            // 如果该评论已被统计，并且标签列表有变动将会影响已经统计的数据
            // 这里需要对统计数据进行修正
            List<Integer> oldTags = FlatListUtils.toList(reviewEntity.getTagList(), Integer::valueOf)
                    .stream()
                    .distinct()
                    .collect(Collectors.toList());
            List<Integer> newTags = request.getTags()
                    .stream()
                    .distinct().collect(Collectors.toList());

            // 新统计的标签
            newTags.removeAll(oldTags);
            statisticsSynchronizer.incrementCompanyTags(reviewEntity.getCompanyId(), newTags, 1);

            // 需要被取消统计的标签
            oldTags.removeAll(request.getTags());
            statisticsSynchronizer.incrementCompanyTags(reviewEntity.getCompanyId(), newTags, -1);
        }
    }

    /**
     * 重新设置上传附件
     *
     * @param reviewId       评论ID
     * @param attachmentUrls 附件Url列表
     */
    private void revalidateAttachmentUrls(Long reviewId, List<String> attachmentUrls) {
        if (null == attachmentUrls) {
            return;
        }

        // 之前的附件
        List<SupplierReviewAttachmentDO> beforeAttachments =
                supplierReviewAttachmentMapper.findByReviewId(reviewId);

        // 增加附件
        List<String> newAttachments = attachmentUrls
                .stream()
                .filter(e -> beforeAttachments.stream().noneMatch(r -> r.getAttachmentUrl().equals(e)))
                .collect(Collectors.toList());
        insertAttachments(reviewId, newAttachments);

        // 移除附件
        List<SupplierReviewAttachmentDO> removedAttachments = beforeAttachments
                .stream()
                .filter(e -> !attachmentUrls.contains(e.getAttachmentUrl()))
                .collect(Collectors.toList());
        removeAttachments(removedAttachments);
    }

    private SupplierReviewDO getSupplierReview(Long reviewId) {
        SupplierReviewDO review = supplierReviewMapper.selectById(reviewId);
        if (null == review) {
            throw new IllegalArgumentException("无法找到供应商评论：'" + reviewId + "'");
        }
        return review;
    }

    /**
     * 插入评论附件
     *
     * @param reviewId       评论ID
     * @param attachmentUrls 附件Url列表
     */
    private void insertAttachments(Long reviewId, List<String> attachmentUrls) {
        if (attachmentUrls.isEmpty()) {
            return;
        }

        //
        // 评论附件表
        for (String url : attachmentUrls) {
            SupplierReviewAttachmentDO attachment = new SupplierReviewAttachmentDO();
            attachment.setReviewId(reviewId);
            attachment.setAttachmentUrl(url);
            supplierReviewAttachmentMapper.insertSelective(attachment);
        }
    }

    /**
     * 移除附件
     *
     * @param attachments 附件列表
     */
    private void removeAttachments(List<SupplierReviewAttachmentDO> attachments) {
        for (SupplierReviewAttachmentDO attachment : attachments) {
            SupplierReviewAttachmentDO update = new SupplierReviewAttachmentDO();
            update.setId(attachment.getId());
            update.setIsDeleted(1);
            supplierReviewAttachmentMapper.updateByIdSelective(update);
        }
    }

    private void validateCreateRequest(CreateSupplierReviewReqDTO request) {
        if (null == request) {
            throw new IllegalArgumentException("评论请求不能为空");
        }
        if (null == request.getCompanyId()) {
            throw new IllegalArgumentException("CompanyId不能为空");
        }
        if (null == request.getPageSource()) {
            throw new IllegalArgumentException("PageSource不能为空");
        }
        if (null == request.getBusinessType()) {
            throw new IllegalArgumentException("BusinessType不能为空");
        }
        if (null == request.getContent()) {
            throw new IllegalArgumentException("Content不能为空");
        }
        if (null == request.getAttachmentUrls()) {
            throw new IllegalArgumentException("AttachmentUrls不能为空");
        }
        if (null == request.getTags()) {
            throw new IllegalArgumentException("Tags不能为空");
        }
    }

    private void validateUpdateRequest(UpdateSupplierReviewReqDTO request) {
        if (null == request) {
            throw new IllegalArgumentException("评论请求不能为空");
        }
        if (null == request.getContent()) {
            throw new IllegalArgumentException("Content不能为空");
        }
        if (null == request.getAttachmentUrls()) {
            throw new IllegalArgumentException("AttachmentUrls不能为空");
        }
        if (null == request.getTags()) {
            throw new IllegalArgumentException("Tags不能为空");
        }
    }

    private void validateRequester(OperatorReqDTO requester) {
        if (null == requester) {
            throw new IllegalArgumentException("请求者不能为空");
        }

        if (null == requester.getOperatorType()) {
            throw new IllegalArgumentException("OperatorType不能为空");
        }

        if (null == requester.getOperatorId()) {
            throw new IllegalArgumentException("OperatorId不能为空");
        }
    }

}
